/// for that package.
pub fn dep_targets(&self, unit: &Unit<'a>) -> Vec<Unit<'a>> {
if unit.profile.run_custom_build {
- return self.dep_run_custom_build(unit, false)
+ return self.dep_run_custom_build(unit)
} else if unit.profile.doc {
return self.doc_deps(unit);
}
})
}).collect::<Vec<_>>();
- // If a target isn't actually a build script itself, then it depends on
- // the build script if there is one.
+ // If this target is a build script, then what we've collected so far is
+ // all we need. If this isn't a build script, then it depends on the
+ // build script if there is one.
if unit.target.is_custom_build() {
return ret
}
- ret.extend(self.build_script_if_run(unit, false));
+ ret.extend(self.dep_build_script(unit));
// If this target is a binary, test, example, etc, then it depends on
// the library of the same package. The call to `resolve.deps` above
return ret
}
- pub fn dep_run_custom_build(&self,
- unit: &Unit<'a>,
- include_overridden: bool) -> Vec<Unit<'a>> {
+ /// Returns the dependencies needed to run a build script.
+ ///
+ /// The `unit` provided must represent an execution of a build script, and
+ /// the returned set of units must all be run before `unit` is run.
+ pub fn dep_run_custom_build(&self, unit: &Unit<'a>) -> Vec<Unit<'a>> {
+ // If this build script's execution has been overridden then we don't
+ // actually depend on anything, we've reached the end of the dependency
+ // chain as we've got all the info we're gonna get.
+ let key = (unit.pkg.package_id().clone(), unit.kind);
+ if self.build_state.outputs.lock().unwrap().contains_key(&key) {
+ return Vec::new()
+ }
+
+ // When not overridden, then the dependencies to run a build script are:
+ //
+ // 1. Compiling the build script itself
+ // 2. For each immediate dependency of our package which has a `links`
+ // key, the execution of that build script.
let not_custom_build = unit.pkg.targets().iter().find(|t| {
!t.is_custom_build()
}).unwrap();
profile: &self.profiles.dev,
..*unit
};
- let mut ret = self.dep_targets(&tmp).iter().filter_map(|unit| {
+ self.dep_targets(&tmp).iter().filter_map(|unit| {
if !unit.target.linkable() || unit.pkg.manifest().links().is_none() {
return None
}
- self.build_script_if_run(unit, include_overridden)
- }).collect::<Vec<_>>();
- ret.push(Unit {
+ self.dep_build_script(unit)
+ }).chain(Some(Unit {
profile: self.build_script_profile(unit.pkg.package_id()),
- kind: Kind::Host,
+ kind: Kind::Host, // build scripts always compiled for the host
..*unit
- });
- return ret
+ })).collect()
}
/// Returns the dependencies necessary to document a package
}
// Be sure to build/run the build script for documented libraries as
- ret.extend(self.build_script_if_run(unit, false));
+ ret.extend(self.dep_build_script(unit));
// If we document a binary, we need the library available
if unit.target.is_bin() {
return ret
}
- /// Returns the build script for a package if that build script is actually
- /// intended to be run for `kind` as part of this compilation.
+ /// If a build script is scheduled to be run for the package specified by
+ /// `unit`, this function will return the unit to run that build script.
///
- /// Build scripts are not run if they are overridden by some global
- /// configuration.
- fn build_script_if_run(&self, unit: &Unit<'a>,
- allow_overridden: bool) -> Option<Unit<'a>> {
- let target = match unit.pkg.targets().iter().find(|t| t.is_custom_build()) {
- Some(t) => t,
- None => return None,
- };
- let key = (unit.pkg.package_id().clone(), unit.kind);
- if !allow_overridden &&
- unit.pkg.manifest().links().is_some() &&
- self.build_state.outputs.lock().unwrap().contains_key(&key) {
- return None
- }
- Some(Unit {
- pkg: unit.pkg,
- target: target,
- profile: &self.profiles.custom_build,
- kind: unit.kind,
+ /// Overriding a build script simply means that the running of the build
+ /// script itself doesn't have any dependencies, so even in that case a unit
+ /// of work is still returned. `None` is only returned if the package has no
+ /// build script.
+ fn dep_build_script(&self, unit: &Unit<'a>) -> Option<Unit<'a>> {
+ unit.pkg.targets().iter().find(|t| t.is_custom_build()).map(|t| {
+ Unit {
+ pkg: unit.pkg,
+ target: t,
+ profile: &self.profiles.custom_build,
+ kind: unit.kind,
+ }
})
}
use super::CommandType;
/// Contains the parsed output of a custom build script.
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Hash)]
pub struct BuildOutput {
/// Paths to pass to rustc with the `-L` flag
pub library_paths: Vec<PathBuf>,
-> CargoResult<(Work, Work, Freshness)> {
let _p = profile::start(format!("build script prepare: {}/{}",
unit.pkg, unit.target.name()));
+ let key = (unit.pkg.package_id().clone(), unit.kind);
+ let overridden = cx.build_state.outputs.lock().unwrap().contains_key(&key);
+ let (work_dirty, work_fresh) = if overridden {
+ (Work::new(|_| Ok(())), Work::new(|_| Ok(())))
+ } else {
+ try!(build_work(cx, unit))
+ };
+
+ // Now that we've prep'd our work, build the work needed to manage the
+ // fingerprint and then start returning that upwards.
+ let (freshness, dirty, fresh) =
+ try!(fingerprint::prepare_build_cmd(cx, unit));
+
+ Ok((work_dirty.then(dirty), work_fresh.then(fresh), freshness))
+}
+
+fn build_work(cx: &mut Context, unit: &Unit) -> CargoResult<(Work, Work)> {
let (script_output, build_output) = {
(cx.layout(unit.pkg, Kind::Host).build(unit.pkg),
cx.layout(unit.pkg, unit.kind).build_out(unit.pkg))
// This information will be used at build-time later on to figure out which
// sorts of variables need to be discovered at that time.
let lib_deps = {
- cx.dep_run_custom_build(unit, true).iter().filter_map(|unit| {
+ cx.dep_run_custom_build(unit).iter().filter_map(|unit| {
if unit.profile.run_custom_build {
Some((unit.pkg.manifest().links().unwrap().to_string(),
unit.pkg.package_id().clone()))
//
// Note that this has to do some extra work just before running the command
// to determine extra environment variables and such.
- let work = Work::new(move |desc_tx| {
+ let dirty = Work::new(move |desc_tx| {
// Make sure that OUT_DIR exists.
//
// If we have an old build directory, then just move it into place,
// Now that we've prepared our work-to-do, we need to prepare the fresh work
// itself to run when we actually end up just discarding what we calculated
// above.
- //
- // Note that the freshness calculation here is the build_cmd freshness, not
- // target specific freshness. This is because we don't actually know what
- // the inputs are to this command!
- //
- // Also note that a fresh build command needs to
- let (freshness, dirty, fresh) =
- try!(fingerprint::prepare_build_cmd(cx, unit));
- let dirty = work.then(dirty);
let fresh = Work::new(move |_tx| {
let (id, pkg_name, build_state, build_output) = all;
let contents = try!(paths::read(&build_output.parent().unwrap()
let output = try!(BuildOutput::parse(&contents, &pkg_name));
build_state.insert(id, kind, output);
Ok(())
- }).then(fresh);
+ });
- Ok((dirty, fresh, freshness))
+ Ok((dirty, fresh))
}
impl BuildState {
debug!("fingerprint at: {}", loc.display());
- let new_fingerprint = try!(calculate_pkg_fingerprint(cx, unit.pkg));
+ // If this build script execution has been overridden, then the fingerprint
+ // is just a hash of what it was overridden with. Otherwise the fingerprint
+ // is that of the entire package itself as we just consider everything as
+ // input to the build script.
+ let new_fingerprint = {
+ let state = cx.build_state.outputs.lock().unwrap();
+ match state.get(&(unit.pkg.package_id().clone(), unit.kind)) {
+ Some(output) => {
+ format!("overridden build state with hash: {}",
+ util::hash_u64(output))
+ }
+ None => try!(calculate_pkg_fingerprint(cx, unit.pkg)),
+ }
+ };
let new_fingerprint = Arc::new(Fingerprint {
rustc: 0,
target: 0,
{running} `[..]rlib -L native=test`
", compiling = COMPILING, running = RUNNING)));
});
+
+test!(adding_an_override_invalidates {
+ let target = ::rustc_host();
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [project]
+ name = "foo"
+ version = "0.5.0"
+ authors = []
+ links = "foo"
+ build = "build.rs"
+ "#)
+ .file("src/lib.rs", "")
+ .file(".cargo/config", "")
+ .file("build.rs", r#"
+ fn main() {
+ println!("cargo:rustc-link-search=native=foo");
+ }
+ "#);
+
+ assert_that(p.cargo_process("build").arg("-v"),
+ execs().with_status(0).with_stdout(&format!("\
+{compiling} foo v0.5.0 ([..]
+{running} `rustc [..]`
+{running} `[..]`
+{running} `rustc [..] -L native=foo`
+", compiling = COMPILING, running = RUNNING)));
+
+ File::create(p.root().join(".cargo/config")).unwrap().write_all(format!("
+ [target.{}.foo]
+ rustc-link-search = [\"native=bar\"]
+ ", target).as_bytes()).unwrap();
+
+ assert_that(p.cargo("build").arg("-v"),
+ execs().with_status(0).with_stdout(&format!("\
+{compiling} foo v0.5.0 ([..]
+{running} `rustc [..] -L native=bar`
+", compiling = COMPILING, running = RUNNING)));
+});
+
+test!(changing_an_override_invalidates {
+ let target = ::rustc_host();
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [project]
+ name = "foo"
+ version = "0.5.0"
+ authors = []
+ links = "foo"
+ build = "build.rs"
+ "#)
+ .file("src/lib.rs", "")
+ .file(".cargo/config", &format!("
+ [target.{}.foo]
+ rustc-link-search = [\"native=foo\"]
+ ", target))
+ .file("build.rs", "");
+
+ assert_that(p.cargo_process("build").arg("-v"),
+ execs().with_status(0).with_stdout(&format!("\
+{compiling} foo v0.5.0 ([..]
+{running} `rustc [..] -L native=foo`
+", compiling = COMPILING, running = RUNNING)));
+
+ File::create(p.root().join(".cargo/config")).unwrap().write_all(format!("
+ [target.{}.foo]
+ rustc-link-search = [\"native=bar\"]
+ ", target).as_bytes()).unwrap();
+
+ assert_that(p.cargo("build").arg("-v"),
+ execs().with_status(0).with_stdout(&format!("\
+{compiling} foo v0.5.0 ([..]
+{running} `rustc [..] -L native=bar`
+", compiling = COMPILING, running = RUNNING)));
+});